package com.example.collection;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author houyangfan
 * @version 1.0
 * @date 2022/7/21 18:50
 */
@Slf4j
public class ListBug {

    public static void main(String[] args) {
        int elementCount = 100000;

        int loopCount = 100000;

        StopWatch stopWatch = new StopWatch();
        stopWatch.start("linkedListGet");
        linkedListGet(elementCount, loopCount);
        stopWatch.stop();

        stopWatch.start("arrayListGet");
        arrayListGet(elementCount, loopCount);
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());

        StopWatch stopWatch2 = new StopWatch();
        stopWatch2.start("linkedListAdd");
        linkedListAdd(elementCount, loopCount);
        stopWatch2.stop();

        stopWatch2.start("arrayListAdd");
        arrayListAdd(elementCount, loopCount);
        stopWatch2.stop();

        System.out.println(stopWatch2.prettyPrint());
    }


    /**
     * 不能直接使用 Arrays.asList 来转换基本类型数组
     */
    public static void testListBug1(){
        //使用 Arrays.asList 方法可以把数组一键转换为 List,但是这样初始化的 List 并不是我们期望的包含 3 个数字的 List
        int[] str = {1, 2, 3};
        List list = Arrays.asList(str);
        log.info("list:{} size:{} class:{}", list, list.size(), list.get(0).getClass());

        //解决方法：
        // ① Arrays.stream
        List list1 = Arrays.stream(str).boxed().collect(Collectors.toList());
        log.info("list:{} size:{} class:{}", list1, list1.size(), list1.get(0).getClass());
        // ② 可以把 int 数组声明为包装类型 String 数组
        String[] arr2 = {"1", "2", "3"};
        List list2 = Arrays.asList(arr2);
        log.info("list:{} size:{} class:{}", list2, list2.size(), list2.get(0).getClass());
        // ③ 可以把 int 数组声明为包装类型 Integer 数组
        Integer[] str2 = {1,2,3};
        List<Integer> list3 = Arrays.asList(str2);
        log.info("list:{} size:{} class:{}", list3, list3.size(), list3.get(0).getClass());

    }
    /**
     *  ① Arrays.asList 返回的 List 不支持增删操作: Arrays.asList 返回的 List 并不是我们期望的 java.util.ArrayList，
     *  而是 Arrays 的内部类 ArrayList,它的add方法就是抛出UnsupportedOperationExceptionc 异常
     *  ② 对原始数组的修改会影响到我们获得的那个 List：ArrayList 其实是直接使用了原始的数组
     */
    public static void testListBug2(){
        String[] arr = {"1", "2", "3"};
        List<String> list = Arrays.asList(arr);
        arr[1] = "4";
        try {
            list.add("5");
        }catch (Exception ex){
            ex.printStackTrace();
        }
        log.info("arr:{} list:{}", Arrays.toString(arr), list);
    }

    public static List<List<Integer>> data = new ArrayList<>();

    public static void testListBug3(){
        for (int i = 0; i < 1000; i++) {
            List<Integer> integers = IntStream.rangeClosed(1, 100000).boxed().collect(Collectors.toList());
            data.add(integers.subList(0, 1));
        }
        System.out.println(data.size());
    }

    /**
     *  修改subList获取到的list 会影响到原始的list
     *  解决方法：
     *  可以将获取到的subList 新建一个ArrayList 作为构造方法传入
     */
    public static void testListBug4(){
        List<Integer> list = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
        List<Integer> subList = list.subList(1,4);
        subList.remove(1);
        System.out.println(subList);
        System.out.println(list);
        list.add(0);
        subList.forEach(integer -> {
            System.out.println(integer);
        });
    }

    /**
     * arrayList 和 linkedList的使用
     * 在这里会发现linkedList 不管是查询操作还是添加操作都和arrayList的效率相差很大
     * linkedList插入的时间复杂度是O(1)的前提是已经有了那个要插入节点的指针。但，在实现的时候，我们需要先通过循环获取到那个节点的 Node，
     * 然后再执行插入操作。前者也是有开销的，
     */

    // LinkedList的访问
    public static void linkedListGet(int elementCount, int loopCount){
        List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
        IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount)));
    }
    //ArrayList访问
    private static void arrayListGet(int elementCount, int loopCount) {
        List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
        IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount)));
    }
    //LinkedList插入
    private static void linkedListAdd(int elementCount, int loopCount) {
        List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
        IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount),1));
    }

    //ArrayList插入
    private static void arrayListAdd(int elementCount, int loopCount) {
        List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
        IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount),1));
    }
}
